package com.varian.system.service.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.Opt;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.varian.mybatis.base.BaseServiceImpl;
import com.varian.mybatis.util.PageUtil;
import com.varian.redis.util.RedisUtil;
import com.varian.system.constant.SystemConstant;
import com.varian.system.convert.SystemConvert;
import com.varian.system.dto.SysBizExceptionDTO;
import com.varian.system.dto.SysBizExceptionPageDTO;
import com.varian.system.entity.SysBizException;
import com.varian.system.mapper.SysBizExceptionMapper;
import com.varian.system.service.ISysBizExceptionService;
import com.varian.system.vo.SysBizExceptionVO;
import com.varian.tool.enums.CommonStatusEnum;
import com.varian.tool.exception.BizException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

/**
 * @author ben
 * @since 2024/9/24
 */
@Service
@RequiredArgsConstructor
public class SysBizExceptionServiceImpl extends BaseServiceImpl<SysBizExceptionMapper, SysBizException> implements ISysBizExceptionService {

    private final SystemConvert baseConvert;

    @Override
    public Page<SysBizExceptionVO> page(SysBizExceptionPageDTO dto) {
        Page<SysBizException> page = page(dto.toPage(), lambdaQuery().getWrapper()
                .likeRight(StrUtil.isNotBlank(dto.getBizCode()), SysBizException::getBizCode, dto.getBizCode())
                .eq(dto.getBizExceptionStatus() != null, SysBizException::getBizExceptionStatus, dto.getBizExceptionStatus())
                .orderByDesc(SysBizException::getId)
        );
        return PageUtil.convert(page, baseConvert::toVO);
    }

    @Override
    public SysBizExceptionVO detail(Long id) {
        SysBizException bizException = validateExistence(id);
        return baseConvert.toVO(bizException);
    }

    @Override
    public boolean add(SysBizExceptionDTO dto) {
        SysBizException bizException = baseConvert.toEntity(dto);
        validateUniqueness(bizException);
        boolean result = save(bizException);
        if (result) {
            String key = SystemConstant.KEY_BIZ_EXCEPTION_MESSAGE.format(bizException.getBizCode());
            RedisUtil.Value.set(key, bizException.getBizMessage());
        }
        return result;
    }

    @Override
    public boolean modify(Long id, SysBizExceptionDTO dto) {
        SysBizException rawBizException = validateExistence(id);
        SysBizException bizException = baseConvert.toEntity(id, dto);
        validateUniqueness(bizException);
        boolean result = updateById(bizException);
        if (result) {
            String key = SystemConstant.KEY_BIZ_EXCEPTION_MESSAGE.format(rawBizException.getBizCode());
            // 启用且修改了业务信息才更新redis
            if (CommonStatusEnum.ENABLED.equals(bizException.getBizExceptionStatus()) &&
                    !StrUtil.equals(rawBizException.getBizMessage(), bizException.getBizMessage())) {
                RedisUtil.Value.set(key, bizException.getBizMessage());
            }
            if (CommonStatusEnum.DISABLED.equals(bizException.getBizExceptionStatus())) {
                RedisUtil.del(key);
            }
        }
        return result;
    }

    @Override
    public boolean delete(Long id) {
        SysBizException bizException = validateExistence(id);
        boolean result = removeById(id);
        if (result) {
            String key = SystemConstant.KEY_BIZ_EXCEPTION_MESSAGE.format(bizException.getBizCode());
            RedisUtil.del(key);
        }
        return result;
    }

    @Override
    public String getMessageByCode(String bizCode) {
        String redisKey = SystemConstant.KEY_BIZ_EXCEPTION_MESSAGE.format(bizCode);
        boolean existed = RedisUtil.hasKey(redisKey);
        if (!existed) {
            RedisUtil.lock(SystemConstant.KEY_BIZ_EXCEPTION_MESSAGE_LOCK.format(bizCode), () -> {
                String bizMessage = getOneOpt(lambdaQuery().getWrapper()
                        .eq(SysBizException::getBizExceptionStatus, CommonStatusEnum.ENABLED)
                        .eq(SysBizException::getBizCode, bizCode))
                        .map(SysBizException::getBizMessage).orElse(null);
                if (StrUtil.isNotBlank(bizMessage)) {
                    RedisUtil.Value.set(redisKey, bizMessage);
                }
            });
        }
        return Opt.<String>ofBlankAble(RedisUtil.Value.get(redisKey)).orElse(null);
    }

    @Override
    protected SysBizException validateExistence(Long id) {
        SysBizException bizException = baseMapper.selectById(id);
        Assert.notNull(bizException, () -> BizException.of(SystemConstant.BIZ_EXCEPTION_NOT_EXIST));
        return bizException;
    }

    @Override
    protected void validateUniqueness(SysBizException bizException) {
        boolean existed = exists(lambdaQuery().getWrapper()
                .eq(SysBizException::getBizCode, bizException.getBizCode())
                .ne(bizException.getId() != null, SysBizException::getId, bizException.getId())
        );
        Assert.isFalse(existed, () -> BizException.of(SystemConstant.BIZ_EXCEPTION_CODE_EXIST));
    }
}
